import { throws } from "assert"
import cheerioModule from "cheerio";
import { val } from "cheerio/lib/api/attributes";
import { getLogger } from "log4js";
import { Collection } from "mongodb"
import { getAxios, sleep } from "../utils"
import { qiniu } from "../utils/qiniuLoad";
import { detailsWeb, heroImgFormat, imgFormat, nameFormat, tierFormat } from "../utils/String";

const logger = getLogger('lxgg')

export interface OPGGTransform {
    _id?: string
    counter: string[]
    against: string[]
    spell: string[]
    skill: string[]
    heroImg: string
}

export interface OPGGModule {
    _id?: string
    counter: string[]
    against: string[]
    spell: string[]
    skill: string[]
    heroImg: string
    name: string
    winner: string
    pick: string
    position: string
    tier: string
    name_zh: string
}

export interface OPGGBasicModule {
    name: string
    winner: string
    pick: string
    tier: string
    position: string
    detailUrl: string
}


export interface OPGGMatch {
    counter: string[]
    against: string[]
}



export interface ExtractInterface {
    extract(url: string): Promise<cheerio.Root>
}

export interface TransformInterface<T> {
    transform(data: cheerio.Root): T
}

export interface LoadInterface {
    load(data: OPGGModule): Promise<void>
}

// 获取网页数据
export class ExtractOPGG implements ExtractInterface {
    async extract(url: string): Promise<cheerio.Root> {
        logger.debug(`ExtractOPGG ${url}`)
        const data = await getAxios(url)
        if (data === '404') throw new Error('axios is error')
        const $ = cheerioModule.load(data)
        return $
    }
}

// 主页采集
export class StratReptile {
    constructor(
        private col: Collection<OPGGBasicModule>
    ) { }
    async start(url: string): Promise<void> {
        const data = await getAxios(url)
        const $ = cheerioModule.load(data)
        const table = $('.detail-ranking__wrapper--ranking .detail-ranking__ratio--tier')
        const lists = $('.detail-ranking__content--champ-list', table)
        const positionArr = ['top', 'jug', 'mid', 'adc', 'sup']
        for (const list of lists) {
            const champio = $('.champion-ratio--normal', list)
            for (const item of champio) {
                let tier = $('.champion-ratio__tier img', item).attr("src")
                let detailUrl = $('a', item).attr('href')
                if (!detailUrl) {
                    detailUrl = ''
                }
                if (!tier) {
                    tier = ''
                } else {
                    tier = tierFormat(tier)
                }
                let pickWinner = $('.champion-ratio__percent div', item).text().split('%')
                const name = nameFormat($('.champion-ratio__name', item).text())
                const res: OPGGBasicModule = {
                    detailUrl: `https://www.op.gg${detailUrl}`,
                    name: name,
                    winner: pickWinner[0],
                    pick: pickWinner[1],
                    tier: tier,
                    position: positionArr[0]
                }
                logger.info(JSON.stringify(res))
                this.col.insertOne(res)
            }
            positionArr.shift()
        }
    }
}

// 详情页 对位信息
export class TransformMatchCline implements TransformInterface<OPGGMatch> {
    transform($: cheerio.Root): OPGGMatch {

        try {
            const tables = $('.matchup-summary__content table')
            const matchup: string[][] = []
            for (const table of tables) {
                const td = $('.matchup-summary__name', table)
                const counter: string[] = []
                for (const item of td) {
                    counter.push(nameFormat($(item).text()))
                }
                matchup.push(counter)
            }

            return { counter: matchup[0], against: matchup[1] }
        } catch (error) {
            throw new Error('TransformMatchCline error')
        }
    }

}

// 装备信息
export class TransoformSpell implements TransformInterface<string[]>{

    transform($: cheerio.Root): string[] {
        try {
            const wrapper = $('.overview__table--stat').eq(1)
            const imgs = $('.overview__item--table img', wrapper)
            const arr: string[] = []
            for (const img of imgs) {
                let uri = $(img).attr('src')
                if (!uri) {
                    throw new Error('TransoformSpell uri is undefined')
                }
                uri = `https:${uri}`
                arr.push(uri)
            }
            return arr
        } catch (error) {
            throw new Error("TransoformSpell is error")
        }
    }

}

// 技能加点
export class TransformSkill implements TransformInterface<string[]> {

    transform($: cheerio.Root): string[] {
        try {
            const wrapper = $('.overview__table--stat').eq(0)
            const tbody = $('tbody', wrapper)
            const skills = $('.overview__item--table', tbody)
            const imgArr: string[] = []
            for (const item of skills) {
                let img = $('img', item).attr('src')
                if (!img) {
                    throw new Error('TransformSkill uri is undefined')
                }
                imgArr.push(`https:${img}`)
            }
            return imgArr
        } catch (error) {
            throw new Error('TransformSkill is error')
        }
    }

}

// 头像
export class TransformHeroImg implements TransformInterface<string>{

    transform($: cheerio.Root): string {
        try {
            let img = $('.champion__image img').attr("src")
            if (!img) {
                throw new Error('TransformSkill uri is undefined')
            }
            img = `https:${img}`
            return img
        } catch (error) {
            throw new Error('TransformHeroImg is error')
        }
    }

}

// tansform解析组合
export class TransformPiple implements TransformInterface<OPGGTransform>{
    constructor(
        private transformMatchCline: TransformInterface<OPGGMatch>,
        private transoformSpell: TransformInterface<string[]>,
        private transformSkill: TransformInterface<string[]>,
        private transformHeroImg: TransformInterface<string>
    ) { }
    transform(data: cheerio.Root): OPGGTransform {
        const match = this.transformMatchCline.transform(data)
        const spell = this.transoformSpell.transform(data)
        const skill = this.transformSkill.transform(data)
        const heroImg = this.transformHeroImg.transform(data)
        const res: OPGGTransform = {
            counter: match.counter,
            against: match.against,
            spell,
            skill,
            heroImg
        }
        logger.debug(`TransformPiple ${JSON.stringify(res)}`)
        return res
    }

}

// 数据添加
export class LoadOPGG implements LoadInterface {

    constructor(
        private opggCol: Collection<OPGGModule>,
        private imgCol: Collection<{ _id: string, url: string }>
    ) { }

    async load(data: OPGGModule): Promise<void> {
        const imgArr: { _id: string, url: string }[] = []
        // 处理 召唤师技能
        const spells = data.spell.map(url => {
            const _id = imgFormat(url)
            imgArr.push({ _id, url })
            // await this.imgCol.updateOne({ _id: _id }, { _id, url: item }, { upsert: true })
            return _id
        })

        // 处理 技能加点
        const skills = data.skill.map(url => {
            const _id = imgFormat(url)
            imgArr.push({ _id, url })
            // await this.imgCol.updateOne({ _id: _id }, { _id, url: item }, { upsert: true })
            return _id
        })

        // 处理 英雄头像
        await this.imgCol.updateOne({ _id: heroImgFormat(data.heroImg) }, { $set: { _id: heroImgFormat(data.heroImg), url: data.heroImg } }, { upsert: true })
        for (const item of imgArr) {
            await this.imgCol.updateOne({ _id: item._id }, { $set: { _id: item._id, url: item.url } }, { upsert: true })
        }
        data.spell = spells
        data.skill = skills
        data.heroImg = heroImgFormat(data.heroImg)
        logger.info(JSON.stringify(data))
    }

}


export class ETL {
    constructor(
        private start: StratReptile,
        private extractImpl: ExtractInterface,
        private transformImpl: TransformInterface<OPGGTransform>,
        private loadImpl: LoadInterface,
        private extractQueue: Collection<OPGGBasicModule>,
        private imgCol: Collection<{ _id: string, url: string }>,
        private imgQueue: Collection<{ _id: string, url: string }>,
        private qiniuLoad: qiniu
    ) { }


    async load(map: Map<string, string>) {
        while (true) {
            const { value } = await this.extractQueue.findOneAndDelete({})
            if (!value) {
                await sleep(100)
                continue
            }
            const url = value.detailUrl
            try {
                const $ = await this.extractImpl.extract(url)
                const data = this.transformImpl.transform($)
                const res: OPGGModule = {
                    _id: `${value.position}_${value.name}`,
                    counter: data.counter,
                    against: data.against,
                    spell: data.spell,
                    skill: data.skill,
                    heroImg: data.heroImg,
                    name: value.name,
                    winner: `${value.winner}%`,
                    pick: `${value.pick}%`,
                    position: value.position,
                    tier: value.tier,
                    name_zh: map.get(value.name) || ""
                }
                await this.loadImpl.load(res)
            } catch (error: any) {
                logger.error(`${error.message} ${url}`)
                this.extractQueue.insertOne(value)
            }
        }
    }

    async img() {
        while (true) {
            const { value } = await this.imgQueue.findOneAndDelete({})
            if (!value) {
                await sleep(10)
                continue
            }
            try {
                await this.imgCol.insertOne(value)

            } catch (error) {
                continue
            }
            this.qiniuLoad.upload(value.url, value._id)
        }
    }

    async etl(map: Map<string, string>) {
        try {
            await this.start.start(`https://www.op.gg/champion/statistics`)
            for (let i = 0; i < 10; i++) {
                this.load(map)
            }
            await this.img()
        } catch (error) {
            
        }
    }
}


